<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width"/>

<meta name="theme-color" content="#414852"/>


<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inconsolata|Itim|Lobster.css">
<link rel="icon" type="image/png" sizes="64x64" href="/images/favicon/xin-64.png" /><link rel="icon" type="image/png" sizes="128x128" href="/images/favicon/xin-128.png" /><link rel="apple-touch-icon" sizes="128x128" href="/images/favicon/xin-128.png" /><link rel="mask-icon" color="white" href="/images/favicon/xin.svg" />  <meta name="description" content="欢迎欣赏新的一年第一篇博文😁">
<meta property="og:type" content="article">
<meta property="og:title" content="JUnit 5 教程">
<meta property="og:url" content="https://jiangtj.com/articles/cherry/junit-5-info/index.html">
<meta property="og:site_name" content="Dnocm ☀">
<meta property="og:description" content="欢迎欣赏新的一年第一篇博文😁">
<meta property="og:locale">
<meta property="og:image" content="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200116113048.png">
<meta property="og:image" content="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200116131303.png">
<meta property="og:image" content="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200116133004.png">
<meta property="og:image" content="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200117141500.png">
<meta property="article:published_time" content="2020-01-15T16:00:00.000Z">
<meta property="article:modified_time" content="2020-09-06T16:00:00.000Z">
<meta property="article:author" content="Mr.J">
<meta property="article:tag" content="Test">
<meta property="article:tag" content="Unit Test">
<meta property="article:tag" content="JUnit">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200116113048.png">  <title>JUnit 5 教程 | Dnocm ☀</title>    <script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","mainEntityOfPage":{"@type":"WebPage","@id":"https://jiangtj.com/articles/cherry/junit-5-info/"},"headline":"JUnit 5 教程","image":[],"datePublished":"2020-01-15T16:00:00.000Z","dateModified":"2020-09-06T16:00:00.000Z","author":{"@type":"Person","name":"Mr.J"},"publisher":{"@type":"Organization","name":"Mr.J","logo":{"@type":"ImageObject","url":"https://jiangtj.com/images/avatar.jpg"}}}</script><script data-ad-client="ca-pub-8243423215749776" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<meta name="google-site-verification" content="TWf4Ma3V7xRcvKFWw_1iOCJKgNJmUUB1CmLucKjvV2A" />
<meta name="baidu-site-verification" content="xtf2YgRPUK" />

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-132230328-1"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-132230328-1');
</script>
<link rel="stylesheet" type="text/css" href="/css/injector/main.css" /><link rel="preload" as="style" href="/css/injector/dark.css" /><link rel="preload" as="style" href="/css/injector/light.css" /><link rel="preload" as="style" href="/css/injector/auto.css" /><!-- hexo injector head_end start --><link rel="manifest" href="/manifest.json" /><!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/rss2.xml" title="Dnocm ☀" type="application/rss+xml">
</head>

<body>

    
  <header class="main-header">
    <div class="header-inner card"><div class="brand">
  <div class="site-meta">
    <div class="custom-logo-site-title">
      <a href="/">
        <span class="site-title">Dnocm ☀</span>
      </a>
    </div>
    
  </div>

  <div class="site-nav-toggle">
    <button>
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="bars" class="svg-inline--fa fa-bars fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"></path></svg>    </button>
  </div>
</div>
<nav class="site-nav">
  <ul class="menu">
    <li class="menu-item menu-item-home"><a href="/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="home" class="svg-inline--fa fa-home fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path></svg>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="tags" class="svg-inline--fa fa-tags fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z"></path></svg>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="th" class="svg-inline--fa fa-th fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M149.333 56v80c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24h101.333c13.255 0 24 10.745 24 24zm181.334 240v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm32-240v80c0 13.255 10.745 24 24 24H488c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24zm-32 80V56c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm-205.334 56H24c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24zM0 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm386.667-56H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zm0 160H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zM181.333 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24z"></path></svg>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="archive" class="svg-inline--fa fa-archive fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 448c0 17.7 14.3 32 32 32h384c17.7 0 32-14.3 32-32V160H32v288zm160-212c0-6.6 5.4-12 12-12h104c6.6 0 12 5.4 12 12v8c0 6.6-5.4 12-12 12H204c-6.6 0-12-5.4-12-12v-8zM480 32H32C14.3 32 0 46.3 0 64v48c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16V64c0-17.7-14.3-32-32-32z"></path></svg>归档</a></li><li class="menu-item menu-item-more"><a href="/more/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="info-circle" class="svg-inline--fa fa-info-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg>More</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg>搜索</a></li>  </ul>
</nav>
</div>
  </header>

  <div class="flex-container">

    

<aside class="sidebar">

  <div class="header-inner card">
    <div class="brand">
  <div class="site-meta">
    <div class="custom-logo-site-title">
      <a href="/">
        <span class="site-title">Dnocm ☀</span>
      </a>
    </div>
    
  </div>

  <div class="site-nav-toggle">
    <button>
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="bars" class="svg-inline--fa fa-bars fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"></path></svg>    </button>
  </div>
</div>
<nav class="site-nav">
  <ul class="menu">
    <li class="menu-item menu-item-home"><a href="/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="home" class="svg-inline--fa fa-home fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path></svg>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="tags" class="svg-inline--fa fa-tags fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z"></path></svg>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="th" class="svg-inline--fa fa-th fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M149.333 56v80c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24h101.333c13.255 0 24 10.745 24 24zm181.334 240v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm32-240v80c0 13.255 10.745 24 24 24H488c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24zm-32 80V56c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm-205.334 56H24c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24zM0 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm386.667-56H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zm0 160H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zM181.333 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24z"></path></svg>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="archive" class="svg-inline--fa fa-archive fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 448c0 17.7 14.3 32 32 32h384c17.7 0 32-14.3 32-32V160H32v288zm160-212c0-6.6 5.4-12 12-12h104c6.6 0 12 5.4 12 12v8c0 6.6-5.4 12-12 12H204c-6.6 0-12-5.4-12-12v-8zM480 32H32C14.3 32 0 46.3 0 64v48c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16V64c0-17.7-14.3-32-32-32z"></path></svg>归档</a></li><li class="menu-item menu-item-more"><a href="/more/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="info-circle" class="svg-inline--fa fa-info-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg>More</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg>搜索</a></li>  </ul>
</nav>
  </div>

  <div class="sidebar-inner card">

    

    <ul class="sidebar-nav">
      <li class="sidebar-nav-toc sidebar-nav-active">
        文章目录
      </li>
      <li class="sidebar-nav-overview">
        站点概览
      </li>
    </ul>
    <div class="post-toc-wrap sidebar-panel sidebar-panel-active">
      <div class="post-toc"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#JUnit-5-%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-number">1.</span> <span class="nav-text">JUnit 5 是什么？</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E5%88%86%E9%92%9F%E6%95%99%E7%A8%8B"><span class="nav-number">2.</span> <span class="nav-text">三分钟教程</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA"><span class="nav-number">2.1.</span> <span class="nav-text">环境搭建</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%AC%AC%E4%B8%80%E4%B8%AA%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B"><span class="nav-number">2.2.</span> <span class="nav-text">第一个测试用例</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%9B%B4%E5%A4%9A%E9%A3%9F%E7%94%A8%E6%96%B9%E6%A1%88"><span class="nav-number">3.</span> <span class="nav-text">更多食用方案</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%88%AB%E5%90%8D"><span class="nav-number">3.1.</span> <span class="nav-text">别名</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%96%AD%E8%A8%80%E3%80%81%E5%81%87%E8%AE%BE"><span class="nav-number">3.2.</span> <span class="nav-text">断言、假设</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%A6%81%E7%94%A8"><span class="nav-number">3.3.</span> <span class="nav-text">禁用</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%B5%8B%E8%AF%95%E6%89%A7%E8%A1%8C%E6%9D%A1%E4%BB%B6"><span class="nav-number">3.4.</span> <span class="nav-text">测试执行条件</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A0%87%E7%AD%BE-%E5%88%86%E7%BB%84"><span class="nav-number">3.5.</span> <span class="nav-text">标签&#x2F;分组</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%A1%BA%E5%BA%8F"><span class="nav-number">3.6.</span> <span class="nav-text">顺序</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="nav-number">3.7.</span> <span class="nav-text">生命周期</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B5%8C%E5%A5%97"><span class="nav-number">3.8.</span> <span class="nav-text">嵌套</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%87%8D%E5%A4%8D%E6%B5%8B%E8%AF%95"><span class="nav-number">3.9.</span> <span class="nav-text">重复测试</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8F%82%E6%95%B0%E6%B5%8B%E8%AF%95"><span class="nav-number">3.10.</span> <span class="nav-text">参数测试</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E6%BA%90"><span class="nav-number">3.10.1.</span> <span class="nav-text">数据源</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%82%E6%95%B0%E8%BD%AC%E6%8D%A2"><span class="nav-number">3.10.2.</span> <span class="nav-text">参数转换</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Dynamic%E6%B5%8B%E8%AF%95"><span class="nav-number">3.11.</span> <span class="nav-text">Dynamic测试</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%89%A9%E5%B1%95"><span class="nav-number">4.</span> <span class="nav-text">扩展</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BB%84%E5%90%88%E6%B3%A8%E8%A7%A3"><span class="nav-number">4.1.</span> <span class="nav-text">组合注解</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Extend-API"><span class="nav-number">4.2.</span> <span class="nav-text">Extend API</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#ExecutionCondition"><span class="nav-number">4.2.1.</span> <span class="nav-text">ExecutionCondition</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#TestInstanceFactory"><span class="nav-number">4.2.2.</span> <span class="nav-text">TestInstanceFactory</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#TestInstancePostProcessor"><span class="nav-number">4.2.3.</span> <span class="nav-text">TestInstancePostProcessor</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#TestInstancePreDestroyCallback"><span class="nav-number">4.2.4.</span> <span class="nav-text">TestInstancePreDestroyCallback</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ParameterResolver"><span class="nav-number">4.2.5.</span> <span class="nav-text">ParameterResolver</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#TestWatcher"><span class="nav-number">4.2.6.</span> <span class="nav-text">TestWatcher</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%9E%E8%B0%83"><span class="nav-number">4.2.7.</span> <span class="nav-text">生命周期回调</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#TestExecutionExceptionHandler"><span class="nav-number">4.2.8.</span> <span class="nav-text">TestExecutionExceptionHandler</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Intercepting-Invocations"><span class="nav-number">4.2.9.</span> <span class="nav-text">Intercepting Invocations</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B8%BA-Test-Templates-%E6%8F%90%E4%BE%9B%E4%B8%8A%E4%B8%8B%E6%96%87"><span class="nav-number">4.2.10.</span> <span class="nav-text">为 Test Templates 提供上下文</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9C%A8%E6%89%A9%E5%B1%95%E4%B8%AD%E4%BF%9D%E6%8C%81%E7%8A%B6%E6%80%81"><span class="nav-number">4.2.11.</span> <span class="nav-text">在扩展中保持状态</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%9C%A8-Spring-%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="nav-number">5.</span> <span class="nav-text">在 Spring 中的使用</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%9C%80%E5%90%8E"><span class="nav-number">6.</span> <span class="nav-text">最后</span></a></li></ol></div>
    </div>
    <div class="site-overview-wrap sidebar-panel">
      <div class="site-overview">
  <div class="site-author">
    
    <img class="site-author-image" src="/images/avatar.jpg"
      alt="Mr.J" />
    
    <p class="site-author-name">Mr.J</p>
    <div class="site-description">何须更问浮生事，只此浮生是梦中</div>
  </div>

  <nav class="site-state">
    
    <div class="site-state-item site-state-posts">
      <a href="/archives/">
        <span class="site-state-item-count">64</span>
        <span class="site-state-item-name">日志</span>
      </a>
    </div>
    

    
    <div class="site-state-item site-state-categories">
      <a href="/categories/">
        <span class="site-state-item-count">8</span>
        <span class="site-state-item-name">分类</span>
      </a>
    </div>
    

    
    <div class="site-state-item site-state-tags">
      <a href="/tags/">
        <span class="site-state-item-count">48</span>
        <span class="site-state-item-name">标签</span>
      </a>
    </div>
    
  </nav>

  <div class="social-links"><a href="mailto:i@dnocm.com" title="mailto:i@dnocm.com" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="envelope" class="svg-inline--fa fa-envelope fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z"></path></svg></a><a href="https://t.me/dnocm" title="https://t.me/dnocm" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="telegram" class="svg-inline--fa fa-telegram fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm121.8 169.9l-40.7 191.8c-3 13.6-11.1 16.9-22.4 10.5l-62-45.7-29.9 28.8c-3.3 3.3-6.1 6.1-12.5 6.1l4.4-63.1 114.9-103.8c5-4.4-1.1-6.9-7.7-2.5l-142 89.4-61.2-19.1c-13.3-4.2-13.6-13.3 2.8-19.7l239.1-92.2c11.1-4 20.8 2.7 17.2 19.5z"></path></svg></a><a href="/rss2.xml" title="/rss2.xml" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="rss" class="svg-inline--fa fa-rss fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M128.081 415.959c0 35.369-28.672 64.041-64.041 64.041S0 451.328 0 415.959s28.672-64.041 64.041-64.041 64.04 28.673 64.04 64.041zm175.66 47.25c-8.354-154.6-132.185-278.587-286.95-286.95C7.656 175.765 0 183.105 0 192.253v48.069c0 8.415 6.49 15.472 14.887 16.018 111.832 7.284 201.473 96.702 208.772 208.772.547 8.397 7.604 14.887 16.018 14.887h48.069c9.149.001 16.489-7.655 15.995-16.79zm144.249.288C439.596 229.677 251.465 40.445 16.503 32.01 7.473 31.686 0 38.981 0 48.016v48.068c0 8.625 6.835 15.645 15.453 15.999 191.179 7.839 344.627 161.316 352.465 352.465.353 8.618 7.373 15.453 15.999 15.453h48.068c9.034-.001 16.329-7.474 16.005-16.504z"></path></svg></a><a href="https://github.com/JiangTJ" title="https://github.com/JiangTJ" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="svg-inline--fa fa-github fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg></a></div><div class="font-size-ol"><a class="font-size-ol-sub">A-</a><span class="font-size-ol-value">14</span><a class="font-size-ol-plus">A+</a></div>
</div>
    </div>

    

  </div>
</aside>

    <div class="flex-main">

      <main>
        
        <div id="content" class="content">
          <div>
  

<article class="post post-type-normal">
  <div class="card post-block">

    

    <header class="post-header">

      
      <h1 class="post-title">
        
        JUnit 5 教程
        
      </h1>
      

      <div class="post-meta">

  
  
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="calendar" class="svg-inline--fa fa-calendar fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M400 64h-48V12c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v52H160V12c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v52H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zm-6 400H54c-3.3 0-6-2.7-6-6V160h352v298c0 3.3-2.7 6-6 6z"></path></svg>
    </span>
    <span class="post-meta-item-text">发表于</span>
    <time title="创建时间：2020-01-16 00:00:00">2020-01-16</time>
  </span>
  

  

  
  
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="folder" class="svg-inline--fa fa-folder fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 128H272l-64-64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V176c0-26.51-21.49-48-48-48z"></path></svg>
    </span>
    <span class="post-meta-item-text">分类于</span>
    <span><a href="/categories/%E5%90%8E%E7%AB%AF/" rel="index"><span>后端</span></a></span>  </span>
  

  
  
  <div class="post-description">欢迎欣赏新的一年第一篇博文😁</div>
  

</div>

    </header>

    <div class="post-body">
      
      <p>JUnit 5 作为新一代的 Java 单元测试框架，提供很多改进。例如对比 <a target="_blank" rel="noopener" href="https://junit.org/junit4/">JUnit4</a> 与 <a target="_blank" rel="noopener" href="https://junit.org/junit5/">JUnit5</a> 的官网，JUnit5 的设计更加简约与时尚，至少不会抗拒阅读的程度了（像破烂一样的网站，看了整个人都难受，不影响效率？不存在的）</p>
<p><img data-src="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200116113048.png"></p>
<p>而且，除此外，他的文档使用了 Asciidoc， 相对于markdown复杂，主要是它还支持具有包含另一个文件内容，这对于写API文档来说挺重要的，有兴趣可以了解下~</p>
<p>Okay， 结束吐槽，让我来看看 JUnit5 到底带来了哪些变化吧</p>
<span id="more"></span>

<h1 id="JUnit-5-是什么？" class="article-heading"><a href="#JUnit-5-是什么？" class="headerlink" title="JUnit 5 是什么？"></a>JUnit 5 是什么？<a class="article-anchor" href="#JUnit-5-是什么？" aria-hidden="true"></a></h1><p>与以往的版本不同，JUnit5 由三个模块模版组成  JUnit Platform + JUnit Jupiter + JUnit Vintage</p>
<ul>
<li>JUnit Platform：运行测试框架的基础服务，定义了一套API，任何实现这套API的测试引擎，都能运行在这之上</li>
<li>JUnit Jupiter：一系列用于编写JUnit5测试或者扩展的组合，同时他的子项目提供了JUnit5测试引擎</li>
<li>JUnit Vintage：提供 JUnit3 和 JUnit4 的测试引擎</li>
</ul>
<h1 id="三分钟教程" class="article-heading"><a href="#三分钟教程" class="headerlink" title="三分钟教程"></a>三分钟教程<a class="article-anchor" href="#三分钟教程" aria-hidden="true"></a></h1><h2 id="环境搭建" class="article-heading"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建<a class="article-anchor" href="#环境搭建" aria-hidden="true"></a></h2><ol>
<li><p>创建你的项目（建议Spring Boot），简单的勾选几个依赖
<img data-src="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200116131303.png"></p>
</li>
<li><p>添加 JUnit5 的依赖（spring boot 2.2 中已默认是Junit5，不需要额外加，详见<a target="_blank" rel="noopener" href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.2-Release-Notes#junit-5">WIKI</a>），</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.junit.jupiter<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>junit-jupiter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>$&#123;latest-version&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>
<p><code>org.junit.jupiter:junit-jupiter</code>已包含了 JUnit Platform，不需要额外声明依赖，一个就够了
<img data-src="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200116133004.png"></p>
</li>
</ol>
<h2 id="第一个测试用例" class="article-heading"><a href="#第一个测试用例" class="headerlink" title="第一个测试用例"></a>第一个测试用例<a class="article-anchor" href="#第一个测试用例" aria-hidden="true"></a></h2><ol>
<li><p>创建一个待测试的工具类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TimeUtils</span> </span>&#123;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">hello</span><span class="hljs-params">(Instant now)</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;现在时间是：&quot;</span> + now.toString();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure></li>
<li><p>创建测试用例</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TimeUtilsTest</span> </span>&#123;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span> </span>&#123;<br>        Instant now = Instant.now();<br>        String expect = <span class="hljs-string">&quot;现在时间是：&quot;</span> + now.toString();<br>        assertEquals(expect, TimeUtils.hello(now));<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure></li>
<li><p>运行测试用例，如果你使用idea，那么直接点旁边的运行按钮，或者使用其它编辑器的功能测试，当然，你还可以选择通过命令行，下载<a target="_blank" rel="noopener" href="https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/">junit-platform-console-standalone</a>，并运行它(不懂)，另一种是<code>mvn test</code>运行测试</p>
</li>
</ol>
<h1 id="更多食用方案" class="article-heading"><a href="#更多食用方案" class="headerlink" title="更多食用方案"></a>更多食用方案<a class="article-anchor" href="#更多食用方案" aria-hidden="true"></a></h1><h2 id="别名" class="article-heading"><a href="#别名" class="headerlink" title="别名"></a>别名<a class="article-anchor" href="#别名" aria-hidden="true"></a></h2><p>测试的Class可以通过添加@DisplayName()，添加别名</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@DisplayName(&quot;时间工具类测试&quot;)</span><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TimeUtilsTest</span> </span>&#123;&#125;<br></code></pre></td></tr></table></figure>

<p>也可以使用@DisplayNameGeneration()，进行更多的配置</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@DisplayNameGeneration(TimeUtils2Test.ReplaceUnderscores.class)</span><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TimeUtils2Test</span> </span>&#123;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">hello</span><span class="hljs-params">()</span> </span>&#123;<br>        Instant now = Instant.now();<br>        String expect = <span class="hljs-string">&quot;现在时间是：&quot;</span> + now.toString();<br>        assertEquals(expect, TimeUtils.hello(now));<br>    &#125;<br>    <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReplaceUnderscores</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DisplayNameGenerator</span>.<span class="hljs-title">ReplaceUnderscores</span> </span>&#123;<br>        <span class="hljs-meta">@Override</span><br>        <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">generateDisplayNameForClass</span><span class="hljs-params">(Class&lt;?&gt; testClass)</span> </span>&#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;哈哈哈&quot;</span>;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="断言、假设" class="article-heading"><a href="#断言、假设" class="headerlink" title="断言、假设"></a>断言、假设<a class="article-anchor" href="#断言、假设" aria-hidden="true"></a></h2><p>测试中核心之一，用于判断是否执行成功，在JUnit5中增加了些对lambdas的支持，例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">asserts</span><span class="hljs-params">()</span> </span>&#123;<br>    assertEquals(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>, () -&gt; <span class="hljs-string">&quot;1要是1&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>另外，还增加了假设</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">assume</span><span class="hljs-params">()</span> </span>&#123;<br>    assumingThat(<span class="hljs-string">&quot;DEV&quot;</span>.equals(System.getenv(<span class="hljs-string">&quot;ENV&quot;</span>)),<br>            () -&gt; &#123;<br>                <span class="hljs-comment">// 如果不为true这里将不执行</span><br>                assertEquals(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>);<br>            &#125;);<br><br>    assumeTrue(<span class="hljs-string">&quot;DEV&quot;</span>.equals(System.getenv(<span class="hljs-string">&quot;ENV&quot;</span>)),<br>            () -&gt; <span class="hljs-string">&quot;Aborting test: not on developer workstation&quot;</span>);<br>    <span class="hljs-comment">// 如果不为true这里将不执行</span><br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="禁用" class="article-heading"><a href="#禁用" class="headerlink" title="禁用"></a>禁用<a class="article-anchor" href="#禁用" aria-hidden="true"></a></h2><p>添加@Disabled()可以禁用测试，这个意义在于某一测试用例遇到问题，临时不执行，等待问题修复后再次使用的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Disabled(&quot;Disabled 因为重复&quot;)</span><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TimeUtilsCopyTest</span> </span>&#123;&#125;<br></code></pre></td></tr></table></figure>

<h2 id="测试执行条件" class="article-heading"><a href="#测试执行条件" class="headerlink" title="测试执行条件"></a>测试执行条件<a class="article-anchor" href="#测试执行条件" aria-hidden="true"></a></h2><p>通过添加 @EnabledOnOs 或者 @DisabledOnOs 来决定在某一操作系统上执行.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-meta">@EnabledOnOs(MAC)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testOnMac</span><span class="hljs-params">()</span> </span>&#123;<br>    log.info(<span class="hljs-string">&quot;exec on mac&quot;</span>);<br>&#125;<br><span class="hljs-meta">@Test</span><br><span class="hljs-meta">@EnabledOnOs(&#123; WINDOWS, LINUX &#125;)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testOnOs</span><span class="hljs-params">()</span> </span>&#123;<br>    log.info(<span class="hljs-string">&quot;exec on windows or linux&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>@EnabledOnJre 和 @DisabledOnJre 可以对java环境判断</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-meta">@EnabledOnJre(JRE.JAVA_8)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testOnJava8</span><span class="hljs-params">()</span> </span>&#123;<br>    log.info(<span class="hljs-string">&quot;exec on java 8&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>@EnabledIfSystemProperty/@DisabledIfSystemProperty 与  @EnabledIfEnvironmentVariable/@DisabledIfEnvironmentVariable 分别判断系统和环境变量，他们的匹配项支持正则表达式</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-meta">@DisabledIfEnvironmentVariable(named = &quot;ENV&quot;, matches = &quot;.*development.*&quot;)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">notOnDeveloperWorkstation</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-comment">// ...</span><br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="标签-分组" class="article-heading"><a href="#标签-分组" class="headerlink" title="标签/分组"></a>标签/分组<a class="article-anchor" href="#标签-分组" aria-hidden="true"></a></h2><p>JUnit5 中支持通过 @Tag() 对测试用例进行分组，例如</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Tag(&quot;conditional&quot;)</span><br><span class="hljs-meta">@Test</span><br><span class="hljs-meta">@EnabledOnOs(MAC)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testOnMac</span><span class="hljs-params">()</span> </span>&#123;<br>    log.info(<span class="hljs-string">&quot;exec on mac&quot;</span>);<br>&#125;<br><span class="hljs-meta">@Tag(&quot;conditional&quot;)</span><br><span class="hljs-meta">@Test</span><br><span class="hljs-meta">@EnabledOnJre(JRE.JAVA_8)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testOnJava8</span><span class="hljs-params">()</span> </span>&#123;<br>    log.info(<span class="hljs-string">&quot;exec on java 8&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>@Tag() 有以下这些语法规则</p>
<ul>
<li>不能为null或者空字符串</li>
<li>不能有空格</li>
<li>不能包含ISO控制符</li>
<li>不能包含保留字符(<code>,</code>,<code>(</code>,<code>)</code>,<code>&amp;</code>,<code>|</code>,<code>!</code>)</li>
</ul>
<h2 id="顺序" class="article-heading"><a href="#顺序" class="headerlink" title="顺序"></a>顺序<a class="article-anchor" href="#顺序" aria-hidden="true"></a></h2><p>添加@TestMethodOrder(MethodOrderer.OrderAnnotation.class)与@Order()，定义测试用例的执行顺序</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@TestMethodOrder(MethodOrderer.OrderAnnotation.class)</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderedTest</span> </span>&#123;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@Order(2)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">emptyValues</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">// perform assertions against empty values</span><br>    &#125;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@Order(1)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">nullValues</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">// perform assertions against null values</span><br>    &#125;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@Order(3)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">validValues</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">// perform assertions against valid values</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="生命周期" class="article-heading"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期<a class="article-anchor" href="#生命周期" aria-hidden="true"></a></h2><p>JUnit5 提供了4个生命周期注解 @BeforeAll @AfterAll @BeforeEach @AfterEach</p>
<ul>
<li>@BeforeAll：在所有的 @Test @RepeatedTest @ParameterizedTest @TestFactory 之前执行</li>
<li>@BeforeEach：在每个测试用例前执行</li>
<li>@AfterAll @AfterEach：与before类似，在测试用例之后执行</li>
</ul>
<p>例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@TestMethodOrder(MethodOrderer.OrderAnnotation.class)</span><br><span class="hljs-meta">@TestInstance(TestInstance.Lifecycle.PER_CLASS)</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LifecycleTest</span> </span>&#123;<br>    <span class="hljs-keyword">int</span> num = <span class="hljs-number">0</span>;<br>    <span class="hljs-meta">@BeforeAll</span><br>    <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initAll</span><span class="hljs-params">()</span> </span>&#123;<br>        log.error(<span class="hljs-string">&quot;initAll&quot;</span>);<br>    &#125;<br>    <span class="hljs-meta">@BeforeEach</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">init</span><span class="hljs-params">()</span> </span>&#123;<br>        log.error(<span class="hljs-string">&quot;init&quot;</span>);<br>    &#125;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@Order(1)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">doTest1</span><span class="hljs-params">()</span> </span>&#123;<br>        log.error(<span class="hljs-string">&quot;num is &quot;</span> + num);<br>        num = <span class="hljs-number">1</span>;<br>        log.error(<span class="hljs-string">&quot;doTest1&quot;</span>);<br>    &#125;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@Order(2)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">doTest2</span><span class="hljs-params">()</span> </span>&#123;<br>        log.error(<span class="hljs-string">&quot;num is &quot;</span> + num);<br>        num = <span class="hljs-number">2</span>;<br>        log.error(<span class="hljs-string">&quot;doTest1&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>除此外，还有@TestInstance()配置，见上面的例子，这个存在两个模式</p>
<ul>
<li>PER_METHOD：每个测试用例执行前，都会创建一个实例（默认，与junit4一致）</li>
<li>PER_CLASS：每个类的测试用例执行前，创建统一的实例</li>
</ul>
<p>上面的例子中，得到的log为：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs log">13:58:03.477 [main] ERROR com.jiangtj.example.junit5.LifecycleTest - initAll<br>13:58:03.485 [main] ERROR com.jiangtj.example.junit5.LifecycleTest - init<br>13:58:03.487 [main] ERROR com.jiangtj.example.junit5.LifecycleTest - num is 0<br>13:58:03.487 [main] ERROR com.jiangtj.example.junit5.LifecycleTest - doTest1<br>13:58:03.494 [main] ERROR com.jiangtj.example.junit5.LifecycleTest - init<br>13:58:03.495 [main] ERROR com.jiangtj.example.junit5.LifecycleTest - num is 1<br>13:58:03.495 [main] ERROR com.jiangtj.example.junit5.LifecycleTest - doTest1<br></code></pre></td></tr></table></figure>

<p><code>doTest1()</code> 的执行，影响到num属性的值，而默认模式下则不会</p>
<h2 id="嵌套" class="article-heading"><a href="#嵌套" class="headerlink" title="嵌套"></a>嵌套<a class="article-anchor" href="#嵌套" aria-hidden="true"></a></h2><p>@Nested() 可以更好的表达测试用例间的关系，例如官方的例子</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@DisplayName(&quot;A stack&quot;)</span><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestingAStackDemo</span> </span>&#123;<br><br>    Stack&lt;Object&gt; stack;<br><br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@DisplayName(&quot;is instantiated with new Stack()&quot;)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">isInstantiatedWithNew</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">new</span> Stack&lt;&gt;();<br>    &#125;<br><br>    <span class="hljs-meta">@Nested</span><br>    <span class="hljs-meta">@DisplayName(&quot;when new&quot;)</span><br>    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WhenNew</span> </span>&#123;<br><br>        <span class="hljs-meta">@BeforeEach</span><br>        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">createNewStack</span><span class="hljs-params">()</span> </span>&#123;<br>            stack = <span class="hljs-keyword">new</span> Stack&lt;&gt;();<br>        &#125;<br><br>        <span class="hljs-meta">@Test</span><br>        <span class="hljs-meta">@DisplayName(&quot;is empty&quot;)</span><br>        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">isEmpty</span><span class="hljs-params">()</span> </span>&#123;<br>            assertTrue(stack.isEmpty());<br>        &#125;<br><br>        <span class="hljs-meta">@Test</span><br>        <span class="hljs-meta">@DisplayName(&quot;throws EmptyStackException when popped&quot;)</span><br>        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">throwsExceptionWhenPopped</span><span class="hljs-params">()</span> </span>&#123;<br>            assertThrows(EmptyStackException.class, stack::pop);<br>        &#125;<br><br>        <span class="hljs-meta">@Test</span><br>        <span class="hljs-meta">@DisplayName(&quot;throws EmptyStackException when peeked&quot;)</span><br>        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">throwsExceptionWhenPeeked</span><span class="hljs-params">()</span> </span>&#123;<br>            assertThrows(EmptyStackException.class, stack::peek);<br>        &#125;<br><br>        <span class="hljs-meta">@Nested</span><br>        <span class="hljs-meta">@DisplayName(&quot;after pushing an element&quot;)</span><br>        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AfterPushing</span> </span>&#123;<br><br>            String anElement = <span class="hljs-string">&quot;an element&quot;</span>;<br><br>            <span class="hljs-meta">@BeforeEach</span><br>            <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">pushAnElement</span><span class="hljs-params">()</span> </span>&#123;<br>                stack.push(anElement);<br>            &#125;<br><br>            <span class="hljs-meta">@Test</span><br>            <span class="hljs-meta">@DisplayName(&quot;it is no longer empty&quot;)</span><br>            <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">isNotEmpty</span><span class="hljs-params">()</span> </span>&#123;<br>                assertFalse(stack.isEmpty());<br>            &#125;<br><br>            <span class="hljs-meta">@Test</span><br>            <span class="hljs-meta">@DisplayName(&quot;returns the element when popped and is empty&quot;)</span><br>            <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">returnElementWhenPopped</span><span class="hljs-params">()</span> </span>&#123;<br>                assertEquals(anElement, stack.pop());<br>                assertTrue(stack.isEmpty());<br>            &#125;<br><br>            <span class="hljs-meta">@Test</span><br>            <span class="hljs-meta">@DisplayName(&quot;returns the element when peeked but remains not empty&quot;)</span><br>            <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">returnElementWhenPeeked</span><span class="hljs-params">()</span> </span>&#123;<br>                assertEquals(anElement, stack.peek());<br>                assertFalse(stack.isEmpty());<br>            &#125;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>我们可以清晰的看到他们之间的关系</p>
<p><img data-src="https://jiangtj-lab.github.io/pic-repo/img-apricot/20200117141500.png"></p>
<h2 id="重复测试" class="article-heading"><a href="#重复测试" class="headerlink" title="重复测试"></a>重复测试<a class="article-anchor" href="#重复测试" aria-hidden="true"></a></h2><p>@RepeatedTest() 执行多次测试，支持name修改名称（具体见官网，觉得没多大意义），另外可以在方法中获取repetitionInfo参数，用于判断当前的执行情况（JUnit5支持注入参数，后续详说）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RepeatedTestsDemo</span> </span>&#123;<br>    <span class="hljs-meta">@RepeatedTest(2)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">repeatedTest</span><span class="hljs-params">()</span> </span>&#123;<br>        log.info(<span class="hljs-string">&quot;done!&quot;</span>);<br>    &#125;<br>    <span class="hljs-meta">@RepeatedTest(2)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">repeatedTest2</span><span class="hljs-params">(RepetitionInfo repetitionInfo)</span> </span>&#123;<br>        <span class="hljs-keyword">int</span> currentRepetition = repetitionInfo.getCurrentRepetition();<br>        <span class="hljs-keyword">int</span> totalRepetitions = repetitionInfo.getTotalRepetitions();<br>        log.info(String.format(<span class="hljs-string">&quot;About to execute repetition %d of %d&quot;</span>, <span class="hljs-comment">//</span><br>                currentRepetition, totalRepetitions));<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="参数测试" class="article-heading"><a href="#参数测试" class="headerlink" title="参数测试"></a>参数测试<a class="article-anchor" href="#参数测试" aria-hidden="true"></a></h2><p>@ParameterizedTest 很实用的注解，需要<code>junit-jupiter-params</code>依赖（我们已经添加了）</p>
<p>它主要是配置@xxxSource，注入参数，以完成测试，参数的注入方式有多种</p>
<h3 id="数据源" class="article-heading"><a href="#数据源" class="headerlink" title="数据源"></a>数据源<a class="article-anchor" href="#数据源" aria-hidden="true"></a></h3><p>@ValueSource 注入String内容，这是最常用的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ParameterizedTest</span><br><span class="hljs-meta">@ValueSource(strings = &#123; &quot;racecar&quot;, &quot;radar&quot;, &quot;able was I ere I saw elba&quot; &#125;)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">palindromes</span><span class="hljs-params">(String candidate)</span> </span>&#123;<br>    log.error(candidate);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>@EnumSource 注入枚举类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ParameterizedTest</span><br><span class="hljs-meta">@EnumSource(TimeUnit.class)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testWithEnumSource</span><span class="hljs-params">(TimeUnit timeUnit)</span> </span>&#123;<br>    log.error(timeUnit.toString());<br>&#125;<br><span class="hljs-meta">@ParameterizedTest</span><br><span class="hljs-meta">@EnumSource(value = TimeUnit.class, names = &#123; &quot;DAYS&quot;, &quot;HOURS&quot; &#125;)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testWithEnumSourceInclude</span><span class="hljs-params">(TimeUnit timeUnit)</span> </span>&#123;<br>    <span class="hljs-comment">// 选择部分</span><br>    log.error(timeUnit.toString());<br>&#125;<br></code></pre></td></tr></table></figure>

<p>@MethodSource 通过方法名注入（我更倾向于使用下面的@ArgumentsSource）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ParameterizedTest</span><br><span class="hljs-meta">@MethodSource(&quot;stringProvider&quot;)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testWithExplicitLocalMethodSource</span><span class="hljs-params">(String argument)</span> </span>&#123;<br>    log.error(argument);<br>&#125;<br><span class="hljs-function"><span class="hljs-keyword">static</span> Stream&lt;String&gt; <span class="hljs-title">stringProvider</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> Stream.of(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-string">&quot;banana&quot;</span>);<br>&#125;<br><br><span class="hljs-meta">@ParameterizedTest</span><br><span class="hljs-meta">@MethodSource(&quot;stringIntAndListProvider&quot;)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testWithMultiArgMethodSource</span><span class="hljs-params">(String str, <span class="hljs-keyword">int</span> num, List&lt;String&gt; list)</span> </span>&#123;<br>    <span class="hljs-comment">// 多参支持</span><br>    log.error(String.format(<span class="hljs-string">&quot;Content: %s is %d, %s&quot;</span>, str, num, String.join(<span class="hljs-string">&quot;,&quot;</span>, list)));<br>&#125;<br><span class="hljs-function"><span class="hljs-keyword">static</span> Stream&lt;Arguments&gt; <span class="hljs-title">stringIntAndListProvider</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> Stream.of(<br>            arguments(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-number">1</span>, Arrays.asList(<span class="hljs-string">&quot;a&quot;</span>, <span class="hljs-string">&quot;b&quot;</span>)),<br>            arguments(<span class="hljs-string">&quot;lemon&quot;</span>, <span class="hljs-number">2</span>, Arrays.asList(<span class="hljs-string">&quot;x&quot;</span>, <span class="hljs-string">&quot;y&quot;</span>))<br>    );<br>&#125;<br></code></pre></td></tr></table></figure>

<p>@CsvSource csv源支持</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ParameterizedTest</span><br><span class="hljs-meta">@CsvSource(&#123;</span><br><span class="hljs-meta">        &quot;apple,         1&quot;,</span><br><span class="hljs-meta">        &quot;banana,        2&quot;,</span><br><span class="hljs-meta">        &quot;&#x27;lemon, lime&#x27;, 0xF1&quot;</span><br><span class="hljs-meta">&#125;)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testWithCsvSource</span><span class="hljs-params">(String fruit, <span class="hljs-keyword">int</span> rank)</span> </span>&#123;<br>    log.error(fruit + rank);<br>&#125;<br></code></pre></td></tr></table></figure>

<p>它也支持从文件导入，例如<code>@CsvFileSource(resources = &quot;/two-column.csv&quot;, numLinesToSkip = 1)</code></p>
<p>@ArgumentsSource 通过自定义的参数提供器导入</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ParameterizedTest</span><br><span class="hljs-meta">@ArgumentsSource(MyArgumentsProvider.class)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">testWithArgumentsSource</span><span class="hljs-params">(String argument)</span> </span>&#123;<br>    log.error(argument);<br>&#125;<br><span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyArgumentsProvider</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ArgumentsProvider</span> </span>&#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> Stream&lt;? extends Arguments&gt; provideArguments(ExtensionContext context) &#123;<br>        <span class="hljs-keyword">return</span> Stream.of(<span class="hljs-string">&quot;apple&quot;</span>, <span class="hljs-string">&quot;banana&quot;</span>).map(Arguments::of);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="参数转换" class="article-heading"><a href="#参数转换" class="headerlink" title="参数转换"></a>参数转换<a class="article-anchor" href="#参数转换" aria-hidden="true"></a></h3><p>为了支持csv，JUnit支持了些内建的转换，详细见文档<a target="_blank" rel="noopener" href="https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests-argument-conversion">writing-tests-parameterized-tests-argument-conversion</a>，如果转换失败，会寻找构造器或者静态构造方法（非私有）中，单String的方法，来转换对应的对象</p>
<blockquote>
<p>内建的转换有必要，但后一种，我宁愿得到报错，而不是转换，隐形的转换往往会导致莫名的问题出现</p>
</blockquote>
<p>所以推荐通过@ConvertWith实现参数类型间的转换</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ParameterizedTest</span><br><span class="hljs-meta">@ValueSource(strings = &#123; &quot;Wow,12&quot;, &quot;radar,50&quot;&#125;)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">toBook</span><span class="hljs-params">(<span class="hljs-meta">@ConvertWith(ToBookConverter.class)</span> Book book)</span> </span>&#123;<br>    log.error(book.toString());<br>&#125;<br><span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ToBookConverter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">SimpleArgumentConverter</span> </span>&#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">protected</span> Object <span class="hljs-title">convert</span><span class="hljs-params">(Object source, Class&lt;?&gt; targetType)</span> </span>&#123;<br>        String value = String.valueOf(source);<br>        String[] split = value.split(<span class="hljs-string">&quot;,&quot;</span>);<br>        <span class="hljs-keyword">return</span> Book.of(split[<span class="hljs-number">0</span>], Integer.parseInt(split[<span class="hljs-number">1</span>]));<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>JUnit中也内置了些转换，如@JavaTimeConversionPattern等</p>
<p>除外，还可以通过@AggregateWith转换或者接收ArgumentsAccessor对象</p>
<h2 id="Dynamic测试" class="article-heading"><a href="#Dynamic测试" class="headerlink" title="Dynamic测试"></a>Dynamic测试<a class="article-anchor" href="#Dynamic测试" aria-hidden="true"></a></h2><p>除了常规的@Test,我们还可以通过@TestFactory来构建整个测试树</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DynamicTestsDemo</span> </span>&#123;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Calculator calculator = <span class="hljs-keyword">new</span> Calculator();<br><br>    <span class="hljs-comment">// This will result in a JUnitException!</span><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">List&lt;String&gt; <span class="hljs-title">dynamicTestsWithInvalidReturnType</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> Arrays.asList(<span class="hljs-string">&quot;Hello&quot;</span>);<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">Collection&lt;DynamicTest&gt; <span class="hljs-title">dynamicTestsFromCollection</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> Arrays.asList(<br>            dynamicTest(<span class="hljs-string">&quot;1st dynamic test&quot;</span>, () -&gt; assertTrue(isPalindrome(<span class="hljs-string">&quot;madam&quot;</span>))),<br>            dynamicTest(<span class="hljs-string">&quot;2nd dynamic test&quot;</span>, () -&gt; assertEquals(<span class="hljs-number">4</span>, calculator.multiply(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>)))<br>        );<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">Iterable&lt;DynamicTest&gt; <span class="hljs-title">dynamicTestsFromIterable</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> Arrays.asList(<br>            dynamicTest(<span class="hljs-string">&quot;3rd dynamic test&quot;</span>, () -&gt; assertTrue(isPalindrome(<span class="hljs-string">&quot;madam&quot;</span>))),<br>            dynamicTest(<span class="hljs-string">&quot;4th dynamic test&quot;</span>, () -&gt; assertEquals(<span class="hljs-number">4</span>, calculator.multiply(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>)))<br>        );<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">Iterator&lt;DynamicTest&gt; <span class="hljs-title">dynamicTestsFromIterator</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> Arrays.asList(<br>            dynamicTest(<span class="hljs-string">&quot;5th dynamic test&quot;</span>, () -&gt; assertTrue(isPalindrome(<span class="hljs-string">&quot;madam&quot;</span>))),<br>            dynamicTest(<span class="hljs-string">&quot;6th dynamic test&quot;</span>, () -&gt; assertEquals(<span class="hljs-number">4</span>, calculator.multiply(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>)))<br>        ).iterator();<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    DynamicTest[] dynamicTestsFromArray() &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DynamicTest[] &#123;<br>            dynamicTest(<span class="hljs-string">&quot;7th dynamic test&quot;</span>, () -&gt; assertTrue(isPalindrome(<span class="hljs-string">&quot;madam&quot;</span>))),<br>            dynamicTest(<span class="hljs-string">&quot;8th dynamic test&quot;</span>, () -&gt; assertEquals(<span class="hljs-number">4</span>, calculator.multiply(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>)))<br>        &#125;;<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">Stream&lt;DynamicTest&gt; <span class="hljs-title">dynamicTestsFromStream</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> Stream.of(<span class="hljs-string">&quot;racecar&quot;</span>, <span class="hljs-string">&quot;radar&quot;</span>, <span class="hljs-string">&quot;mom&quot;</span>, <span class="hljs-string">&quot;dad&quot;</span>)<br>            .map(text -&gt; dynamicTest(text, () -&gt; assertTrue(isPalindrome(text))));<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">Stream&lt;DynamicTest&gt; <span class="hljs-title">dynamicTestsFromIntStream</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">// Generates tests for the first 10 even integers.</span><br>        <span class="hljs-keyword">return</span> IntStream.iterate(<span class="hljs-number">0</span>, n -&gt; n + <span class="hljs-number">2</span>).limit(<span class="hljs-number">10</span>)<br>            .mapToObj(n -&gt; dynamicTest(<span class="hljs-string">&quot;test&quot;</span> + n, () -&gt; assertTrue(n % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>)));<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">Stream&lt;DynamicTest&gt; <span class="hljs-title">generateRandomNumberOfTests</span><span class="hljs-params">()</span> </span>&#123;<br><br>        <span class="hljs-comment">// Generates random positive integers between 0 and 100 until</span><br>        <span class="hljs-comment">// a number evenly divisible by 7 is encountered.</span><br>        Iterator&lt;Integer&gt; inputGenerator = <span class="hljs-keyword">new</span> Iterator&lt;Integer&gt;() &#123;<br><br>            Random random = <span class="hljs-keyword">new</span> Random();<br>            <span class="hljs-keyword">int</span> current;<br><br>            <span class="hljs-meta">@Override</span><br>            <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">hasNext</span><span class="hljs-params">()</span> </span>&#123;<br>                current = random.nextInt(<span class="hljs-number">100</span>);<br>                <span class="hljs-keyword">return</span> current % <span class="hljs-number">7</span> != <span class="hljs-number">0</span>;<br>            &#125;<br><br>            <span class="hljs-meta">@Override</span><br>            <span class="hljs-function"><span class="hljs-keyword">public</span> Integer <span class="hljs-title">next</span><span class="hljs-params">()</span> </span>&#123;<br>                <span class="hljs-keyword">return</span> current;<br>            &#125;<br>        &#125;;<br><br>        <span class="hljs-comment">// Generates display names like: input:5, input:37, input:85, etc.</span><br>        Function&lt;Integer, String&gt; displayNameGenerator = (input) -&gt; <span class="hljs-string">&quot;input:&quot;</span> + input;<br><br>        <span class="hljs-comment">// Executes tests based on the current input value.</span><br>        ThrowingConsumer&lt;Integer&gt; testExecutor = (input) -&gt; assertTrue(input % <span class="hljs-number">7</span> != <span class="hljs-number">0</span>);<br><br>        <span class="hljs-comment">// Returns a stream of dynamic tests.</span><br>        <span class="hljs-keyword">return</span> DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor);<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">Stream&lt;DynamicNode&gt; <span class="hljs-title">dynamicTestsWithContainers</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> Stream.of(<span class="hljs-string">&quot;A&quot;</span>, <span class="hljs-string">&quot;B&quot;</span>, <span class="hljs-string">&quot;C&quot;</span>)<br>            .map(input -&gt; dynamicContainer(<span class="hljs-string">&quot;Container &quot;</span> + input, Stream.of(<br>                dynamicTest(<span class="hljs-string">&quot;not null&quot;</span>, () -&gt; assertNotNull(input)),<br>                dynamicContainer(<span class="hljs-string">&quot;properties&quot;</span>, Stream.of(<br>                    dynamicTest(<span class="hljs-string">&quot;length &gt; 0&quot;</span>, () -&gt; assertTrue(input.length() &gt; <span class="hljs-number">0</span>)),<br>                    dynamicTest(<span class="hljs-string">&quot;not empty&quot;</span>, () -&gt; assertFalse(input.isEmpty()))<br>                ))<br>            )));<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">DynamicNode <span class="hljs-title">dynamicNodeSingleTest</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> dynamicTest(<span class="hljs-string">&quot;&#x27;pop&#x27; is a palindrome&quot;</span>, () -&gt; assertTrue(isPalindrome(<span class="hljs-string">&quot;pop&quot;</span>)));<br>    &#125;<br><br>    <span class="hljs-meta">@TestFactory</span><br>    <span class="hljs-function">DynamicNode <span class="hljs-title">dynamicNodeSingleContainer</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> dynamicContainer(<span class="hljs-string">&quot;palindromes&quot;</span>,<br>            Stream.of(<span class="hljs-string">&quot;racecar&quot;</span>, <span class="hljs-string">&quot;radar&quot;</span>, <span class="hljs-string">&quot;mom&quot;</span>, <span class="hljs-string">&quot;dad&quot;</span>)<br>                .map(text -&gt; dynamicTest(text, () -&gt; assertTrue(isPalindrome(text)))<br>        ));<br>    &#125;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<p>还未看过源码，但目测@Test是由内建的转换器，转换成DynamicNode，然后再执行。使用@TestFactory，tree型的代码也是种选择，再维护上，不差于@Test的常规方案</p>
<h1 id="扩展" class="article-heading"><a href="#扩展" class="headerlink" title="扩展"></a>扩展<a class="article-anchor" href="#扩展" aria-hidden="true"></a></h1><p>与 Junit4 不同，Junit5 提供了一个统一的一个扩展API。不过在之前，先看下另一个 Junit5 的重要特性–组合注解</p>
<h2 id="组合注解" class="article-heading"><a href="#组合注解" class="headerlink" title="组合注解"></a>组合注解<a class="article-anchor" href="#组合注解" aria-hidden="true"></a></h2><p>在官方文档中，这部分与注解部分一同讲的，但我将它移到此处，因为绝大多数情况下，他都是与扩展API一同使用。</p>
<p>组合注解，顾名思义，当一个注解上存在其他的Junit注解时，同时也继承这些注解的语义</p>
<p>例如：组合Tag与Test注解</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Target(&#123; ElementType.TYPE, ElementType.METHOD &#125;)</span><br><span class="hljs-meta">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="hljs-meta">@Tag(&quot;fast&quot;)</span><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-meta">@interface</span> Fast &#123;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Fast</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">asserts</span><span class="hljs-params">()</span> </span>&#123;<br>    assertTrue(<span class="hljs-keyword">true</span>);<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="Extend-API" class="article-heading"><a href="#Extend-API" class="headerlink" title="Extend API"></a>Extend API<a class="article-anchor" href="#Extend-API" aria-hidden="true"></a></h2><p>在 Junit5 中通过 <code>@ExtendWith</code> 注解实现添加扩展。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@ExtendWith(DatabaseExtension.class)</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimpleTest</span> </span>&#123;<br>  <span class="hljs-comment">// code</span><br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseExtension</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">BeforeAllCallback</span>, <span class="hljs-title">AfterAllCallback</span> </span>&#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">beforeAll</span><span class="hljs-params">(ExtensionContext extensionContext)</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;<br>        log.info(<span class="hljs-string">&quot;连接数据库&quot;</span>);<br>    &#125;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">afterAll</span><span class="hljs-params">(ExtensionContext extensionContext)</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;<br>        log.info(<span class="hljs-string">&quot;关闭数据库&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p><code>@ExtendWith</code> 提供了扩展的入口，具体的实现通过实现对应的接口，例如上面的 <code>DatabaseExtension</code> 实现 <code>BeforeAllCallback</code>，<code>AfterAllCallback</code></p>
<p>在Junit中，存在许多扩展接口</p>
<h3 id="ExecutionCondition" class="article-heading"><a href="#ExecutionCondition" class="headerlink" title="ExecutionCondition"></a>ExecutionCondition<a class="article-anchor" href="#ExecutionCondition" aria-hidden="true"></a></h3><p>定义执行条件，满足条件时才能执行，下面是一个例子</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Target(&#123; ElementType.TYPE, ElementType.METHOD &#125;)</span><br><span class="hljs-meta">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="hljs-meta">@ExtendWith(PassConditionalExtension.class)</span><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-meta">@interface</span> Pass &#123;<br>    <span class="hljs-function">String <span class="hljs-title">value</span><span class="hljs-params">()</span></span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PassConditionalExtension</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExecutionCondition</span> </span>&#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> ConditionEvaluationResult <span class="hljs-title">evaluateExecutionCondition</span><span class="hljs-params">(ExtensionContext context)</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> AnnotationUtils.findAnnotation(context.getElement(), Pass.class)<br>                .map(Pass::value)<br>                .filter(<span class="hljs-string">&quot;我很帅&quot;</span>::equals)<br>                .map(item -&gt; ConditionEvaluationResult.enabled(<span class="hljs-string">&quot;pass&quot;</span>))<br>                .orElse(ConditionEvaluationResult.disabled(<span class="hljs-string">&quot;pass is not okay!&quot;</span>));<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConditionalTest</span> </span>&#123;<br>    <span class="hljs-meta">@Pass(&quot;密码不对不执行&quot;)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">notExec</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">// code...</span><br>    &#125;<br>    <span class="hljs-meta">@Pass(&quot;我很帅&quot;)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">exec</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">// code...</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="TestInstanceFactory" class="article-heading"><a href="#TestInstanceFactory" class="headerlink" title="TestInstanceFactory"></a>TestInstanceFactory<a class="article-anchor" href="#TestInstanceFactory" aria-hidden="true"></a></h3><p>定义测试实例，只能用于class上，暂时想不到例子，跳过~~</p>
<h3 id="TestInstancePostProcessor" class="article-heading"><a href="#TestInstancePostProcessor" class="headerlink" title="TestInstancePostProcessor"></a>TestInstancePostProcessor<a class="article-anchor" href="#TestInstancePostProcessor" aria-hidden="true"></a></h3><p>对测试实例处理，通常用于注入依赖，暂时想不到例子，跳过~~</p>
<h3 id="TestInstancePreDestroyCallback" class="article-heading"><a href="#TestInstancePreDestroyCallback" class="headerlink" title="TestInstancePreDestroyCallback"></a>TestInstancePreDestroyCallback<a class="article-anchor" href="#TestInstancePreDestroyCallback" aria-hidden="true"></a></h3><p>当测试实例销毁前调用，暂时想不到例子，跳过~~</p>
<h3 id="ParameterResolver" class="article-heading"><a href="#ParameterResolver" class="headerlink" title="ParameterResolver"></a>ParameterResolver<a class="article-anchor" href="#ParameterResolver" aria-hidden="true"></a></h3><p>处理参数，见下面例子</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Target(ElementType.PARAMETER)</span><br><span class="hljs-meta">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="hljs-keyword">public</span> <span class="hljs-meta">@interface</span> BookInject &#123;<br>    <span class="hljs-function">String <span class="hljs-title">title</span><span class="hljs-params">()</span></span>;<br>    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">price</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> 0</span>;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BookParameterResolver</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ParameterResolver</span> </span>&#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">supportsParameter</span><span class="hljs-params">(ParameterContext parameterContext, ExtensionContext extensionContext)</span> <span class="hljs-keyword">throws</span> ParameterResolutionException </span>&#123;<br>        <span class="hljs-keyword">return</span> parameterContext.isAnnotated(BookInject.class);<br>    &#125;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">resolveParameter</span><span class="hljs-params">(ParameterContext parameterContext, ExtensionContext extensionContext)</span> <span class="hljs-keyword">throws</span> ParameterResolutionException </span>&#123;<br>        <span class="hljs-keyword">return</span> parameterContext.findAnnotation(BookInject.class)<br>                .map(book -&gt; Book.of(book.title(), book.price()))<br>                .orElse(<span class="hljs-keyword">null</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BookParameterTest</span> </span>&#123;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@ExtendWith(BookParameterResolver.class)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">exec</span><span class="hljs-params">(<span class="hljs-meta">@BookInject(title = &quot;删库&quot;)</span> Book book)</span> </span>&#123;<br>        log.info(book.toString());<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="TestWatcher" class="article-heading"><a href="#TestWatcher" class="headerlink" title="TestWatcher"></a>TestWatcher<a class="article-anchor" href="#TestWatcher" aria-hidden="true"></a></h3><p>监听测试用例的执行结果</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LogTestWatcher</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TestWatcher</span> </span>&#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testSuccessful</span><span class="hljs-params">(ExtensionContext context)</span> </span>&#123;<br>        log.info(<span class="hljs-string">&quot;wow, 成功了！&quot;</span>);<br>    &#125;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testAborted</span><span class="hljs-params">(ExtensionContext context, Throwable cause)</span> </span>&#123;<br>        <span class="hljs-comment">// 终止</span><br>    &#125;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testDisabled</span><span class="hljs-params">(ExtensionContext context, Optional&lt;String&gt; reason)</span> </span>&#123;<br>        <span class="hljs-comment">// 取消（跳过）</span><br>    &#125;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testFailed</span><span class="hljs-params">(ExtensionContext context, Throwable cause)</span> </span>&#123;<br>        <span class="hljs-comment">// 失败</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="生命周期回调" class="article-heading"><a href="#生命周期回调" class="headerlink" title="生命周期回调"></a>生命周期回调<a class="article-anchor" href="#生命周期回调" aria-hidden="true"></a></h3><p>在一开始的例子中就是生命周期的回调，这里不写例子拉，他们执行的先后顺序如下</p>
<ul>
<li>BeforeAllCallback<ul>
<li>BeforeEachCallback<ul>
<li>BeforeTestExecutionCallback</li>
<li>AfterTestExecutionCallback</li>
</ul>
</li>
<li>AfterEachCallback</li>
</ul>
</li>
<li>AfterAllCallback</li>
</ul>
<h3 id="TestExecutionExceptionHandler" class="article-heading"><a href="#TestExecutionExceptionHandler" class="headerlink" title="TestExecutionExceptionHandler"></a>TestExecutionExceptionHandler<a class="article-anchor" href="#TestExecutionExceptionHandler" aria-hidden="true"></a></h3><p>处理异常，如果存在一些自定义的运行时异常，这是很有用的，可以做些处理</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IgnoreExceptionExtension</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TestExecutionExceptionHandler</span> </span>&#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleTestExecutionException</span><span class="hljs-params">(ExtensionContext context, Throwable throwable)</span> <span class="hljs-keyword">throws</span> Throwable </span>&#123;<br>        <span class="hljs-keyword">if</span> (throwable <span class="hljs-keyword">instanceof</span> Exception) &#123;<br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br>        <span class="hljs-keyword">throw</span> throwable;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimpleTest</span> </span>&#123;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@ExtendWith(IgnoreExceptionExtension.class)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">exec2</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Exception(<span class="hljs-string">&quot;被忽略&quot;</span>);<br>    &#125;<br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-meta">@ExtendWith(IgnoreExceptionExtension.class)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">exec3</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>&#123;<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Throwable(<span class="hljs-string">&quot;不被忽略&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="Intercepting-Invocations" class="article-heading"><a href="#Intercepting-Invocations" class="headerlink" title="Intercepting Invocations"></a>Intercepting Invocations<a class="article-anchor" href="#Intercepting-Invocations" aria-hidden="true"></a></h3><p>拦截测试方法，类似于 Spring 中的 AOP</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@ExtendWith(MyInvocationInterceptorTest.LogInvocationInterceptor.class)</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyInvocationInterceptorTest</span> </span>&#123;<br><br>    <span class="hljs-meta">@ParameterizedTest</span><br>    <span class="hljs-meta">@ValueSource(strings = &#123; &quot;racecar&quot;, &quot;radar&quot;, &quot;able was I ere I saw elba&quot; &#125;)</span><br>    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">showParameterized</span><span class="hljs-params">(String candidate)</span> </span>&#123;<br>        log.error(candidate);<br>    &#125;<br><br><br>    <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LogInvocationInterceptor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">InvocationInterceptor</span> </span>&#123;<br>        <span class="hljs-meta">@Override</span><br>        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">interceptTestTemplateMethod</span><span class="hljs-params">(Invocation&lt;Void&gt; invocation,</span></span><br><span class="hljs-params"><span class="hljs-function">                                                ReflectiveInvocationContext&lt;Method&gt; invocationContext,</span></span><br><span class="hljs-params"><span class="hljs-function">                                                ExtensionContext extensionContext)</span> <span class="hljs-keyword">throws</span> Throwable </span>&#123;<br>            Method executable = invocationContext.getExecutable();<br>            List&lt;Object&gt; arguments = invocationContext.getArguments();<br>            Class&lt;?&gt; targetClass = invocationContext.getTargetClass();<br>            log.info(<span class="hljs-string">&quot;executable method: &quot;</span> + executable.getName());<br>            log.info(<span class="hljs-string">&quot;arguments: &quot;</span> + arguments.stream().map(String::valueOf).collect(Collectors.joining()));<br>            log.info(<span class="hljs-string">&quot;targetClass: &quot;</span> + targetClass.getName());<br>            log.info(<span class="hljs-string">&quot;invocation.proceed() start&quot;</span>);<br>            invocation.proceed();<br>            log.info(<span class="hljs-string">&quot;invocation.proceed() end&quot;</span>);<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>InvocationInterceptor 中有多个方法 <code>interceptBeforeAllMethod</code> <code>interceptTestMethod</code> <code>interceptTestTemplateMethod</code> 等，分别在不同的时候拦截，里中 <code>@ParameterizedTest</code> 继承 <code>@TestTemplate</code> 所以使用 <code>interceptTestTemplateMethod</code></p>
<p>拦截器中一般会传入这几个变量：</p>
<ul>
<li>invocation: 测试请求，只有<code>proceed()</code>代表执行</li>
<li>invocationContext: 测试请求的上下文</li>
<li>extensionContext: 扩展的上下文</li>
</ul>
<h3 id="为-Test-Templates-提供上下文" class="article-heading"><a href="#为-Test-Templates-提供上下文" class="headerlink" title="为 Test Templates 提供上下文"></a>为 Test Templates 提供上下文<a class="article-anchor" href="#为-Test-Templates-提供上下文" aria-hidden="true"></a></h3><p>上面提到了 <code>@ParameterizedTest</code> 是由 <code>@TestTemplate</code>， 而 <code>@TestTemplate</code> 至少需要一个 <code>TestTemplateInvocationContextProvider</code> 提供时执行，在 <code>@ParameterizedTest</code> 中我们可以看到，<code>@ParameterizedTest</code> 由 <code>ParameterizedTestExtension.class</code> 提供测试的参数</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@TestTemplate</span><br><span class="hljs-meta">@ExtendWith(ParameterizedTestExtension.class)</span><br><span class="hljs-keyword">public</span> <span class="hljs-meta">@interface</span> ParameterizedTest &#123;<br>  <span class="hljs-comment">// ...</span><br>&#125;<br></code></pre></td></tr></table></figure>

<p>所以，相对于我写例子，直接学习它的源码可能更好，这是真实的案例，下面是 <code>ParameterizedTestExtension.class</code> 部分内容</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ParameterizedTestExtension</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TestTemplateInvocationContextProvider</span> </span>&#123;<br><br>  <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String METHOD_CONTEXT_KEY = <span class="hljs-string">&quot;context&quot;</span>;<br><br>  <span class="hljs-comment">// 在 TestTemplateInvocationContextProvider 提供两个方法，这是其中一个</span><br>  <span class="hljs-comment">// 用于判断是否支持该扩展，例如下面两判断分别是不存在测试方法与不存在注解@ParameterizedTest时不执行（按道理不能能出现的情况。。。）</span><br>  <span class="hljs-meta">@Override</span><br>  <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">supportsTestTemplate</span><span class="hljs-params">(ExtensionContext context)</span> </span>&#123;<br>    <span class="hljs-keyword">if</span> (!context.getTestMethod().isPresent()) &#123;<br>      <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;<br>    &#125;<br>    Method testMethod = context.getTestMethod().get();<br>    <span class="hljs-keyword">if</span> (!isAnnotated(testMethod, ParameterizedTest.class)) &#123;<br>      <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;<br>    &#125;<br><br>    ParameterizedTestMethodContext methodContext = <span class="hljs-keyword">new</span> ParameterizedTestMethodContext(testMethod);<br>    Preconditions.condition(methodContext.hasPotentiallyValidSignature(),<br>      () -&gt; String.format(<br>        <span class="hljs-string">&quot;@ParameterizedTest method [%s] declares formal parameters in an invalid order: &quot;</span><br>            + <span class="hljs-string">&quot;argument aggregators must be declared after any indexed arguments &quot;</span><br>            + <span class="hljs-string">&quot;and before any arguments resolved by another ParameterResolver.&quot;</span>,<br>        testMethod.toGenericString()));<br>    getStore(context).put(METHOD_CONTEXT_KEY, methodContext);<br>    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br>  &#125;<br><br><br>  <span class="hljs-comment">// 这是另一个方法</span><br>  <span class="hljs-comment">// 提供测试的参数</span><br>  <span class="hljs-comment">// 返回一个Stream，简单的样式是 Stream.of(invocationContext(&quot;apple&quot;), invocationContext(&quot;banana&quot;));</span><br>  <span class="hljs-comment">// ParameterizedTestExtension中比较复杂，大概是 获取提供值(获取参数提供器 -&gt; 消费注解) -&gt; 获取并消费参数 -&gt; 构建InvocationContext</span><br>  <span class="hljs-meta">@Override</span><br>  <span class="hljs-function"><span class="hljs-keyword">public</span> Stream&lt;TestTemplateInvocationContext&gt; <span class="hljs-title">provideTestTemplateInvocationContexts</span><span class="hljs-params">(ExtensionContext extensionContext)</span> </span>&#123;<br><br>    Method templateMethod = extensionContext.getRequiredTestMethod();<br>    String displayName = extensionContext.getDisplayName();<br>    ParameterizedTestMethodContext methodContext = getStore(extensionContext)<span class="hljs-comment">//</span><br>        .get(METHOD_CONTEXT_KEY, ParameterizedTestMethodContext.class);<br>    ParameterizedTestNameFormatter formatter = createNameFormatter(templateMethod, displayName);<br>    AtomicLong invocationCount = <span class="hljs-keyword">new</span> AtomicLong(<span class="hljs-number">0</span>);<br><br>    <span class="hljs-comment">// @formatter:off</span><br>    <span class="hljs-keyword">return</span> findRepeatableAnnotations(templateMethod, ArgumentsSource.class)<br>        .stream()<br>        .map(ArgumentsSource::value)<br>        .map(<span class="hljs-keyword">this</span>::instantiateArgumentsProvider)<br>        .map(provider -&gt; AnnotationConsumerInitializer.initialize(templateMethod, provider))<br>        .flatMap(provider -&gt; arguments(provider, extensionContext))<br>        .map(Arguments::get)<br>        .map(arguments -&gt; consumedArguments(arguments, methodContext))<br>        .map(arguments -&gt; createInvocationContext(formatter, methodContext, arguments))<br>        .peek(invocationContext -&gt; invocationCount.incrementAndGet())<br>        .onClose(() -&gt;<br>            Preconditions.condition(invocationCount.get() &gt; <span class="hljs-number">0</span>,<br>                <span class="hljs-string">&quot;Configuration error: You must configure at least one set of arguments for this @ParameterizedTest&quot;</span>));<br>    <span class="hljs-comment">// @formatter:on</span><br>  &#125;<br><br>  <span class="hljs-comment">// ...</span><br><br>&#125;<br><br></code></pre></td></tr></table></figure>

<h3 id="在扩展中保持状态" class="article-heading"><a href="#在扩展中保持状态" class="headerlink" title="在扩展中保持状态"></a>在扩展中保持状态<a class="article-anchor" href="#在扩展中保持状态" aria-hidden="true"></a></h3><p>熟悉前端的知道在 vue 或者 react 中都会涉及到状态 state 的保持，在junit 5 中也提供了类似的API <code>Store</code> （连名字都差不多。。。），大致上你可以理解为Map这类的东西，在 <code>ParameterizedTestExtension</code> 中也使用它存储了 <code>METHOD_CONTEXT_KEY</code></p>
<h1 id="在-Spring-中的使用" class="article-heading"><a href="#在-Spring-中的使用" class="headerlink" title="在 Spring 中的使用"></a>在 Spring 中的使用<a class="article-anchor" href="#在-Spring-中的使用" aria-hidden="true"></a></h1><blockquote>
<p>未完待续</p>
</blockquote>
<h1 id="最后" class="article-heading"><a href="#最后" class="headerlink" title="最后"></a>最后<a class="article-anchor" href="#最后" aria-hidden="true"></a></h1><p>一个疑问，JUnit5 的注解风格和 Spring 为何如此接近。。。</p>
<ul>
<li>例子源码：<a target="_blank" rel="noopener" href="https://github.com/jiangtj-lab/junit5-demo">https://github.com/jiangtj-lab/junit5-demo</a></li>
</ul>
      
    </div>

    







<div>
  <ul class="post-copyright">
    <li class="post-copyright-author">
      <strong>本文作者： </strong>
      Mr.J
    </li>
    <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="https://jiangtj.com/articles/cherry/junit-5-info/">https://jiangtj.com/articles/cherry/junit-5-info/</a>
    <li class="post-copyright-license">
      <strong>版权声明： </strong>
      本博客所有文章除特别声明外，均采用 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="creative-commons" class="svg-inline--fa fa-creative-commons fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M245.83 214.87l-33.22 17.28c-9.43-19.58-25.24-19.93-27.46-19.93-22.13 0-33.22 14.61-33.22 43.84 0 23.57 9.21 43.84 33.22 43.84 14.47 0 24.65-7.09 30.57-21.26l30.55 15.5c-6.17 11.51-25.69 38.98-65.1 38.98-22.6 0-73.96-10.32-73.96-77.05 0-58.69 43-77.06 72.63-77.06 30.72-.01 52.7 11.95 65.99 35.86zm143.05 0l-32.78 17.28c-9.5-19.77-25.72-19.93-27.9-19.93-22.14 0-33.22 14.61-33.22 43.84 0 23.55 9.23 43.84 33.22 43.84 14.45 0 24.65-7.09 30.54-21.26l31 15.5c-2.1 3.75-21.39 38.98-65.09 38.98-22.69 0-73.96-9.87-73.96-77.05 0-58.67 42.97-77.06 72.63-77.06 30.71-.01 52.58 11.95 65.56 35.86zM247.56 8.05C104.74 8.05 0 123.11 0 256.05c0 138.49 113.6 248 247.56 248 129.93 0 248.44-100.87 248.44-248 0-137.87-106.62-248-248.44-248zm.87 450.81c-112.54 0-203.7-93.04-203.7-202.81 0-105.42 85.43-203.27 203.72-203.27 112.53 0 202.82 89.46 202.82 203.26-.01 121.69-99.68 202.82-202.84 202.82z"></path></svg>BY-NC-SA</a> 许可协议。转载请注明出处！    </li>
  </ul>
</div>


    
<div class="post-tags">
  
  <a href="/tags/Test/" rel="tag"># Test</a>
  
  <a href="/tags/Unit-Test/" rel="tag"># Unit Test</a>
  
  <a href="/tags/JUnit/" rel="tag"># JUnit</a>
  
</div>


    
    <div class="reward-container"><div>Buy me a cup of milkshake 🍨.</div><div class="reward-box"><div class="reward-box-card wechat">
  <img class="reward-image" src="/images/wechatpay.png" />
  
</div>
<div class="reward-box-card alipay">
  <img class="reward-image" src="/images/alipay.png" />
  
  <a class="reward-image-url" href="HTTPS://QR.ALIPAY.COM/FKX06416WJNHOWKMRQQFFE">点击跳转</a>
  
</div>
<div class="reward-box-card paypal">
  <img class="reward-image" src="/images/paypal.png" />
  
  <a class="reward-image-url" target="_blank" rel="noopener" href="https://www.paypal.me/jiangtj">https://www.paypal.me/jiangtj</a>
  
</div>
<div class="reward-box-card kofi">
  <img class="reward-image" src="/images/kofi.png" />
  
  <a class="reward-image-url" target="_blank" rel="noopener" href="https://ko-fi.com/jiangtj">https://ko-fi.com/jiangtj</a>
  
</div>
</div><div class="reward-group"><a class="reward-button wechat"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="weixin" class="svg-inline--fa fa-weixin fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M385.2 167.6c6.4 0 12.6.3 18.8 1.1C387.4 90.3 303.3 32 207.7 32 100.5 32 13 104.8 13 197.4c0 53.4 29.3 97.5 77.9 131.6l-19.3 58.6 68-34.1c24.4 4.8 43.8 9.7 68.2 9.7 6.2 0 12.1-.3 18.3-.8-4-12.9-6.2-26.6-6.2-40.8-.1-84.9 72.9-154 165.3-154zm-104.5-52.9c14.5 0 24.2 9.7 24.2 24.4 0 14.5-9.7 24.2-24.2 24.2-14.8 0-29.3-9.7-29.3-24.2.1-14.7 14.6-24.4 29.3-24.4zm-136.4 48.6c-14.5 0-29.3-9.7-29.3-24.2 0-14.8 14.8-24.4 29.3-24.4 14.8 0 24.4 9.7 24.4 24.4 0 14.6-9.6 24.2-24.4 24.2zM563 319.4c0-77.9-77.9-141.3-165.4-141.3-92.7 0-165.4 63.4-165.4 141.3S305 460.7 397.6 460.7c19.3 0 38.9-5.1 58.6-9.9l53.4 29.3-14.8-48.6C534 402.1 563 363.2 563 319.4zm-219.1-24.5c-9.7 0-19.3-9.7-19.3-19.6 0-9.7 9.7-19.3 19.3-19.3 14.8 0 24.4 9.7 24.4 19.3 0 10-9.7 19.6-24.4 19.6zm107.1 0c-9.7 0-19.3-9.7-19.3-19.6 0-9.7 9.7-19.3 19.3-19.3 14.5 0 24.4 9.7 24.4 19.3.1 10-9.9 19.6-24.4 19.6z"></path></svg></span></a><a class="reward-button alipay"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="alipay" class="svg-inline--fa fa-alipay fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M377.74 32H70.26C31.41 32 0 63.41 0 102.26v307.48C0 448.59 31.41 480 70.26 480h307.48c38.52 0 69.76-31.08 70.26-69.6-45.96-25.62-110.59-60.34-171.6-88.44-32.07 43.97-84.14 81-148.62 81-70.59 0-93.73-45.3-97.04-76.37-3.97-39.01 14.88-81.5 99.52-81.5 35.38 0 79.35 10.25 127.13 24.96 16.53-30.09 26.45-60.34 26.45-60.34h-178.2v-16.7h92.08v-31.24H88.28v-19.01h109.44V92.34h50.92v50.42h109.44v19.01H248.63v31.24h88.77s-15.21 46.62-38.35 90.92c48.93 16.7 100.01 36.04 148.62 52.74V102.26C447.83 63.57 416.43 32 377.74 32zM47.28 322.95c.99 20.17 10.25 53.73 69.93 53.73 52.07 0 92.58-39.68 117.87-72.9-44.63-18.68-84.48-31.41-109.44-31.41-67.45 0-79.35 33.06-78.36 50.58z"></path></svg></span></a><a class="reward-button paypal"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="paypal" class="svg-inline--fa fa-paypal fa-w-12 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M111.4 295.9c-3.5 19.2-17.4 108.7-21.5 134-.3 1.8-1 2.5-3 2.5H12.3c-7.6 0-13.1-6.6-12.1-13.9L58.8 46.6c1.5-9.6 10.1-16.9 20-16.9 152.3 0 165.1-3.7 204 11.4 60.1 23.3 65.6 79.5 44 140.3-21.5 62.6-72.5 89.5-140.1 90.3-43.4.7-69.5-7-75.3 24.2zM357.1 152c-1.8-1.3-2.5-1.8-3 1.3-2 11.4-5.1 22.5-8.8 33.6-39.9 113.8-150.5 103.9-204.5 103.9-6.1 0-10.1 3.3-10.9 9.4-22.6 140.4-27.1 169.7-27.1 169.7-1 7.1 3.5 12.9 10.6 12.9h63.5c8.6 0 15.7-6.3 17.4-14.9.7-5.4-1.1 6.1 14.4-91.3 4.6-22 14.3-19.7 29.3-19.7 71 0 126.4-28.8 142.9-112.3 6.5-34.8 4.6-71.4-23.8-92.6z"></path></svg></span></a><a class="reward-button kofi"><span><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="coffee" class="svg-inline--fa fa-coffee fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M192 384h192c53 0 96-43 96-96h32c70.6 0 128-57.4 128-128S582.6 32 512 32H120c-13.3 0-24 10.7-24 24v232c0 53 43 96 96 96zM512 96c35.3 0 64 28.7 64 64s-28.7 64-64 64h-32V96h32zm47.7 384H48.3c-47.6 0-61-64-36-64h583.3c25 0 11.8 64-35.9 64z"></path></svg></span></a></div></div>    

    
    <footer class="post-footer">
      
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/articles/beechnut/intersection-observer-info/" rel="prev" title="Intersection Observer 简介">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="chevron-left" class="svg-inline--fa fa-chevron-left fa-w-10 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"></path></svg>
      Intersection Obser...
    </a></div>
      <div class="post-nav-item">
    <a href="/articles/cherry/github-packages-npm/" rel="next" title="发布 npm 包到 GitHub Packages">
      发布 npm 包到 GitHub P...
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="chevron-right" class="svg-inline--fa fa-chevron-right fa-w-10 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"></path></svg>
    </a></div>
    </div>    </footer>
    
  </div>
</article>
</div>
        </div>
        




<div class="card comment-body disqus active">
<div id="disqus_thread"></div>
<script>
var disqus_config = function () {
this.page.url = 'https://jiangtj.com/articles/cherry/junit-5-info/';;  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'articles/cherry/junit-5-info/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://jiangtj.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a target="_blank" rel="noopener" href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    </div>



      </main>

      <footer class="footer">
        <div>
          <div class="copyright">
  
  &copy; 
  <span>2021</span>
  <span class="with-love">
    <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="heart" class="svg-inline--fa fa-heart fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"></path></svg>
    <i class="[object Object]"></i>
  </span>
  <span class="author">Mr.J</span>
</div>

<div class="powered-by">
  由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> 强力驱动 v5.4.0
</div>

<span class="post-meta-divider">|</span>

<div class="theme-info">
  主题 – <a href="https://github.com/JiangTJ/hexo-theme-cake" class="theme-link" rel="noopener" target="_blank">Cake</a> v4.0.0
</div>

        </div>
      </footer>

    </div>

  </div>

  <div class="search-popup">
  <div class="search-header">
    <span class="search-icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg></span>
    <div class="search-input-container">
      <input autocomplete="off" autocorrect="off" autocapitalize="none" placeholder="搜索..."
        spellcheck="false" type="text" id="search-input">
    </div>
    <span class="popup-btn-close"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="times-circle" class="svg-inline--fa fa-times-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path></svg></span>
  </div>
  <div id="search-result"></div>
</div>
<div class="search-pop-overlay"></div>
<script id="ribbon" size="300" alpha="0.6" zIndex="-1" src="//cdn.jsdelivr.net/gh/theme-next/theme-next-canvas-ribbon@1/canvas-ribbon.js"></script><script>
((window.gitter = {}).chat = {}).options = {
  room: 'jiangtj/Lobby',
  activationElement: false
};
</script><script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script><script>
  (function () {
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    bp.src = (curProtocol === 'https') ? 'https://zz.bdstatic.com/linksubmit/push.js' : 'http://push.zhanzhang.baidu.com/push.js';
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
  })();
</script>
<div class="moon-menu">
  <div class="moon-menu-items">
    
    <div id="moon-menu-item-back2bottom" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-down" class="svg-inline--fa fa-chevron-down fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"></path></svg>    </div>
    
    <div id="moon-menu-item-back2top" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-up" class="svg-inline--fa fa-chevron-up fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z"></path></svg>    </div>
    
    <div id="moon-menu-item-chat" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="comments" class="svg-inline--fa fa-comments fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M416 192c0-88.4-93.1-160-208-160S0 103.6 0 192c0 34.3 14.1 65.9 38 92-13.4 30.2-35.5 54.2-35.8 54.5-2.2 2.3-2.8 5.7-1.5 8.7S4.8 352 8 352c36.6 0 66.9-12.3 88.7-25 32.2 15.7 70.3 25 111.3 25 114.9 0 208-71.6 208-160zm122 220c23.9-26 38-57.7 38-92 0-66.9-53.5-124.2-129.3-148.1.9 6.6 1.3 13.3 1.3 20.1 0 105.9-107.7 192-240 192-10.8 0-21.3-.8-31.7-1.9C207.8 439.6 281.8 480 368 480c41 0 79.1-9.2 111.3-25 21.8 12.7 52.1 25 88.7 25 3.2 0 6.1-1.9 7.3-4.8 1.3-2.9.7-6.3-1.5-8.7-.3-.3-22.4-24.2-35.8-54.5z"></path></svg>    </div>
    
    <div id="moon-menu-item-switch_color" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="adjust" class="svg-inline--fa fa-adjust fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M8 256c0 136.966 111.033 248 248 248s248-111.034 248-248S392.966 8 256 8 8 119.033 8 256zm248 184V72c101.705 0 184 82.311 184 184 0 101.705-82.311 184-184 184z"></path></svg>    </div>
    
  </div>
  <div class="moon-menu-button">
    <svg class="moon-menu-bg">
      <circle class="moon-menu-cricle" cx="50%" cy="50%" r="44%"></circle>
      <circle class="moon-menu-border" cx="50%" cy="50%" r="48%"></circle>
    </svg>
    <div class="moon-menu-content">
      <div class="moon-menu-icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="ellipsis-v" class="svg-inline--fa fa-ellipsis-v fa-w-6 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512"><path fill="currentColor" d="M96 184c39.8 0 72 32.2 72 72s-32.2 72-72 72-72-32.2-72-72 32.2-72 72-72zM24 80c0 39.8 32.2 72 72 72s72-32.2 72-72S135.8 8 96 8 24 40.2 24 80zm0 352c0 39.8 32.2 72 72 72s72-32.2 72-72-32.2-72-72-72-72 32.2-72 72z"></path></svg></div>
      <div class="moon-menu-text"></div>
    </div>
  </div>
</div><script src="/js/injector.js"></script>
<!-- hexo injector body_end start --><script>
// Check that service workers are supported
if ('serviceWorker' in navigator) {
  // Use the window load event to keep the page load performant
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js');
  });
}
</script>
<!-- hexo injector body_end end --></body>

</html>
